import hashlib
import hmac
import requests.auth
import time
import sys
import urllib.parse
import json

from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin2.api_client.common import (SyncQueryer)
from enum import Enum

class OrderSide(Enum):
  ORDER_SIDE_BUY = "ORDER_SIDE_BUY"
  ORDER_SIDE_SELL = "ORDER_SIDE_SELL"

class OrderType(Enum):
  ORDER_TYPE_MARKET = "ORDER_TYPE_MARKET"
  ORDER_TYPE_LIMIT = "ORDER_TYPE_LIMIT"
  ORDER_TYPE_LIQUIDATION = "ORDER_TYPE_LIQUIDATION"
  ORDER_TYPE_STOP_MARKET = "ORDER_TYPE_STOP_MARKET"
  ORDER_TYPE_STOP_LIMIT = "ORDER_TYPE_STOP_LIMIT"
  ORDER_TYPE_MARKET_IF_TOUCHED = "ORDER_TYPE_MARKET_IF_TOUCHED"

class TimeInForce(Enum):
  TIME_IN_FORCE_GOOD_TILL_CANCEL = "TIME_IN_FORCE_GOOD_TILL_CANCEL"
  TIME_IN_FORCE_IMMEDIATE_OR_CANCEL = "TIME_IN_FORCE_IMMEDIATE_OR_CANCEL"
  TIME_IN_FORCE_FILL_OR_KILL = "TIME_IN_FORCE_FILL_OR_KILL"

class ExecInst(Enum):
  EXEC_INST_PARTICIPATE_DO_NOT_INITIATE = "EXEC_INST_PARTICIPATE_DO_NOT_INITIATE"
  EXEC_INST_REDUCE_ONLY = "EXEC_INST_REDUCE_ONLY"
  EXEC_INST_CLOSE = "EXEC_INST_CLOSE"

def generate_api_key_signature(secret, verb, url, expires, data):
  parsed_url = urllib.parse.urlparse(url)
  path = parsed_url.path
  if parsed_url.query:
    path = path + '?' + parsed_url.query
  if isinstance(data, (bytes, bytearray)):
    data = data.decode('utf8')
  message = verb + path + str(expires) + data
  signature = hmac.new(
      bytes(secret, 'utf8'), bytes(message, 'utf8'), digestmod=hashlib.sha256)
  return signature.hexdigest()


class PrexAuth(requests.auth.AuthBase):
  def __init__(self, key_file):
    self._key = AuthKey.from_file(key_file)

  def _gen_signature(self, data):
    signature = hmac.new(self._key.secret_key_bytes, data, digestmod=hashlib.sha256)
    return signature.hexidgest()

  def __call__(self, req: requests.PreparedRequest):
    expires = int(round(time.time()) + 5)
    req.headers['api-expires'] = str(expires)
    req.headers['api-key'] = self._key.access_key
    req.headers['api-signature'] = generate_api_key_signature(
        self._key.secret_key, req.method, req.url, expires, req.body or '')

    # print(req.url)
    # print(req.headers)
    # print(req.body)

    return req


class PrexPrivateClient():
  def __init__(self, key_file, mea):
    self.key_file = key_file
    self.url = 'https://mmapi.sandbox.prexapi.com'
    self.auth = PrexAuth(key_file)
    self.query = SyncQueryer(api_host=self.url, auth=self.auth)

  def get_open_order(self):
    method = "GET"
    path = "/api/v1/order"
    response = self.query.query(method=method, path=path, timeout=1)
    print("{} {}".format(response.request.method, response.request.url))
    return response

  def get_wallet(self):
    method = "GET"
    path = "/api/v1/wallet"
    response = self.query.query(method=method, path=path, timeout=1)
    return response

  def get_position(self):
    method = "GET"
    path = "/api/v1/position"
    response = self.query.query(method=method, path=path, timeout=1)
    return response

  def submit_order(
      self,
      symbol: str,
      qty: str,
      order_side: OrderSide,
      order_type: OrderType,
      price: str = None,
      time_in_force: TimeInForce = None,
      exec_inst: ExecInst = None,
      exec_insts: list[ExecInst] = None,
      client_order_id: str = None,
    ):
    assert symbol, "symbol must be not None and not empty!"
    assert qty, "qty must be not None and not empty!"
    assert order_side, "order_side must be not None!"
    assert order_type, "order_type must be not None!"

    #"execInst": exec_inst and exec_inst.value,
    method = "POST"
    path = "/api/v1/order/submit"
    json = {
      "symbol": symbol,
      "qty": qty,
      "orderSide": order_side.value,
      "orderType": order_type.value,
      "price": price,
      "timeInForce": time_in_force and time_in_force.value,
      "execInsts": exec_insts and [e.value for e in exec_insts],
      #"execInsts": '["EXEC_INST_PARTICIPATE_DO_NOT_INITIATE"]',
      "clientOrderId": str(int(time.time() * 1000000)),
    }

    response = self.query.query(method=method, path=path, json=json, timeout=1)
    print(response.request.url)
    print(response.request.headers)
    print(response.request.body)
    return response

  def cancel_order(
      self,
      symbol: str,
      order_id: str = None,
      client_order_id: str = None,
    ):
    assert symbol, "symbol must be not None and not empty!"

    #"execInst": exec_inst and exec_inst.value,
    method = "POST"
    path = "/api/v1/order/cancel"
    json = {
      "symbol": symbol,
      "orderId": order_id,
      "clientOrderId": client_order_id,
    }

    response = self.query.query(method=method, path=path, json=json, timeout=1)
    print(response.request.url)
    print(response.request.headers)
    print(response.request.body)
    return response

  def amend_order(
      self,
      symbol: str,
      qty: str,
      price: str = None,
      order_id: str = None,
      client_order_id: str = None,
    ):
    assert symbol, "symbol must be not None and not empty!"
    assert qty, "qty must be not None and not empty!"

    #"execInst": exec_inst and exec_inst.value,
    method = "POST"
    path = "/api/v1/order/amend"
    json = {
      "symbol": symbol,
      "orderQty": qty,
      "price": price,
      "orderId": order_id,
      "clientOrderId": str(int(time.time() * 1000000)),
    }

    response = self.query.query(method=method, path=path, json=json, timeout=1)
    print("{} {}".format(response.request.method, response.request.url))
    #print(response.request.headers)
    print(response.request.body)
    return response

  def convert(
      self,
      from_currency: str,
      to_currency: str,
      from_amount: float,
    ):
    method = "POST"
    path = "/api/v1/conversion/execute"
    json = {
      "fromCurrency": from_currency,
      "toCurrency": to_currency,
      "fromAmount": str(from_amount),
    }

    response = self.query.query(method=method, path=path, json=json, timeout=1)
    return response


if __name__ == "__main__":
  key_path = sys.argv[1]
  config = {}
  config["key_path"] = key_path
  mea = "Futures.Prex.v1"
  prex = PrexPrivateClient(key_path, mea)
  resp = prex.get_open_order()
  #info = resp.json()
  #for r in info["order"]:
  #  if "EOSUSDT.PERP" not in r["symbol"]:
  #    continue
  #  print(json.dumps(r, indent=2))
  #time.sleep(1)
  #resp = prex.get_wallet()
  #resp = prex.convert("BTC", "USDT", 0.00021314)
  #resp = prex.submit_order("EOSUSDT.PERP", "15", OrderSide.ORDER_SIDE_BUY, OrderType.ORDER_TYPE_LIMIT, "0.7", TimeInForce.TIME_IN_FORCE_GOOD_TILL_CANCEL, None, [ExecInst.EXEC_INST_PARTICIPATE_DO_NOT_INITIATE])
  #resp = prex.amend_order("EOSUSDT.PERP", "10", "0.7", "259af4ba-fd60-11ad-4ad0-9108b06e4bda")
  #resp = prex.cancel_order("EOSUSDT.PERP", "259af4ba-fd60-11ad-4ad0-9108b06e4bda", None);
  print(json.dumps(resp.json(), indent=2))
  #time.sleep(1)
  #resp = prex.get_open_order()
  #info = resp.json()
  #for r in info["order"]:
  #  if "EOSUSDT.PERP" not in r["symbol"]:
  #    continue
  #  print(json.dumps(r, indent=2))
